package framework.util.aopLog;

import com.alibaba.fastjson.JSON;
import framework.bo.SysUser;
import framework.common.Common;
import framework.common.ResponseVo;
import framework.util.http._HttpRequestUtil;
import framework.util.http._HttpUtil;
import framework.util.aopLog.bo.SysMethodExceptionLog;
import framework.util.aopLog.bo.SysMethodLog;
import framework.util.aopLog.service.SysMethodExceptionLogService;
import framework.util.aopLog.service.SysMethodLogService;
import framework.util.shiro._ShiroUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 记录日志的AOP实现类，所有关于@Log的日志记录实现都在这里
 *
 * @author 吴宇亮 on 2019/6/23 8:47
 */
@Aspect
public class LogAopImpl {

    @Resource
    private SysMethodLogService sysMethodLogService;
    @Resource
    private SysMethodExceptionLogService sysMethodExceptionLogService;

    /**
     * 记录调用者的IP、userId、request请求参数列表、调用方法、调用方法的入参列表、方法执行的返回值、执行用时、操作时间
     * @param pjp 切点对象
     */
    @Around("@annotation(framework.util.aopLog.Log)")
    public ResponseVo recordInvokedMethodMessage(ProceedingJoinPoint pjp) {
        Object target = pjp.getTarget(); //获取要真正被调用的对象
        Object[] args = pjp.getArgs(); //获取入参
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); //获取要准备执行的方法的签名

        SysMethodLog log = new SysMethodLog();
        SysUser currentUser = _ShiroUtil.getCurrentUser();
        if(null != currentUser){
            log.setOperatorId(currentUser.getLid());
        }
        LocalDateTime now = LocalDateTime.now();
        log.setOperatorTime(now.format(DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss")));

        Object methodInvokedResult = null; //方法的执行结果
        try {
            //被调用的类
            Class<?> clazz = target.getClass();
            log.setClassName(clazz.getName());
            //准备要执行的方法
            Method currentMethod = clazz.getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
            log.setMethodName(currentMethod.getName());
            //准备要执行的方法的参数列表
            Class<?>[] parameterTypes = currentMethod.getParameterTypes();
            log.setMethodParamList(JSON.toJSONString(parameterTypes));
            //方法的入参列表（序列化时过滤掉request和response，参考博客：https://blog.csdn.net/hbn1326317071/article/details/83818059）
            List<Object> logArgs = Arrays.asList(args).stream()
                    .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
                    .collect(Collectors.toList());
            log.setInvokedParamList(JSON.toJSONString(logArgs));
            //reqeust请求参数列表和ip地址
            List<Object> requestList = Arrays.asList(args).stream()
                    .filter(arg -> arg instanceof HttpServletRequest)
                    .collect(Collectors.toList());
            if(!CollectionUtils.isEmpty(requestList)){
                HttpServletRequest request = (HttpServletRequest)requestList.get(0);
                //请求参数
                Map<String, String> requestMap = _HttpRequestUtil.getRequestMap(request);
                log.setRequestParamList(JSON.toJSONString(requestMap));
                //IP
                String ipAdrress = _HttpUtil.getIpAdrress(request);
                log.setOperatorIp(ipAdrress);
            }

            //模块名和功能名
            Log annotation = currentMethod.getAnnotation(Log.class);
            log.setModelName(annotation.modelName());
            log.setFuncName(annotation.funcName());

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        int isException = Common.IS_NO;
        Throwable exception = null;

        long begin = System.nanoTime();
        try {
            //执行目标方法
            methodInvokedResult = (0 != args.length) ?  pjp.proceed(args) : pjp.proceed();
        } catch (Throwable e) {
            //如果发生了异常
            exception = e;
            isException = Common.IS_YEW;
        }
        long end = System.nanoTime();
        //执行用时（毫秒数）
        log.setUseTime(Long.valueOf(TimeUnit.MILLISECONDS.convert((end - begin), TimeUnit.NANOSECONDS)).doubleValue());
        //是否发生异常
        log.setIsException(isException);
        sysMethodLogService.add(log);

        if(Common.IS_YEW == isException){
            SysMethodExceptionLog exceptionLog = new SysMethodExceptionLog();
            exceptionLog.setLogLid(log.getLid());

            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            exception.printStackTrace(pw);
            exceptionLog.setMessage(sw.toString());
            sysMethodExceptionLogService.add(exceptionLog);
        }

        if(methodInvokedResult instanceof ResponseVo){
            ResponseVo responseVo = (ResponseVo) methodInvokedResult;
            log.setReturnValue(JSON.toJSONString(responseVo));
            sysMethodLogService.update(log);
            return responseVo;
        }else{
            return null;
        }
    }

}
